home *** CD-ROM | disk | FTP | other *** search
/ Gold Medal Software 1 / Gold Medal Software Volume 1 (Gold Medal) (1994).iso / prog / objeng.arj / OBJENG.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-08  |  20.0 KB  |  693 lines

  1. /* **************************************************************************
  2.  * Copyright (c) 1993, BSDI.  All rights reserved.
  3.  * Library Name...  Object Engine 
  4.  * File Name......  objeng.cpp
  5.  * Comments.......  This file contains the definitions for the core of the
  6.  *                  object engine.
  7.  *
  8.  * ***************************************************************************/
  9.  
  10. #ifdef _Windows
  11. #include <windows.h>
  12. #else
  13. #include <conio.h>
  14. #endif
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include "objeng.hpp"
  18.  
  19. extern ObjEng objeng;
  20.  
  21. //-----------------------------------------------------------
  22. #ifdef _Windows
  23. ObjEng::ObjEng(char *clientName, int shareMode, int bufsize, int maxContainer, int saveChanges, char *fd)
  24. #else
  25. ObjEng::ObjEng(int bufsize, int maxContainer, int saveChanges, char *fd)
  26. #endif
  27. {
  28.     PXSetDefaults(bufsize, maxContainer, maxContainer * 2, PXDEFAULT, PXDEFAULT, PXDEFAULT);
  29. #ifdef _Windows
  30.     if ((status = PXWinInit(clientName, shareMode)) != PXSUCCESS) return;
  31. #else
  32.     if ((status = PXInit()) != PXSUCCESS) return;
  33. #endif
  34.     OESaveChanges = saveChanges;
  35.  
  36.     strcpy(filedir, fd); 
  37.     int i = strlen(filedir);   //Assuming usually WON'T have \
  38.     if (filedir[i - 1] != '\\') 
  39.     {
  40.         filedir[i] = '\\';
  41.         filedir[i + 1] = 0;
  42.     }
  43.     for (i = 0; i < OE_MAXTABLES; i++)
  44.     {
  45.         memset(&RegisteredTables[i], 0, sizeof(OEHandle));
  46.     }
  47.     TableInit();
  48. }
  49.  
  50.  
  51. //-----------------------------------------------------------
  52. #ifdef _Windows
  53. ObjEng::ObjEng(char *clientName)  //Uses all defaults.
  54. #else
  55. ObjEng::ObjEng()  //Uses all defaults.
  56. #endif
  57. {
  58. #ifdef _Windows
  59.     if ((status = PXWinInit(clientName, PXSHARED)) != PXSUCCESS) return;
  60. #else
  61.     if ((status = PXInit()) != PXSUCCESS) return;
  62. #endif
  63.     OESaveChanges = TRUE;
  64.     strcpy(filedir, ".\\");
  65.     TableInit();
  66. }
  67.  
  68.  
  69. //-----------------------------------------------------------
  70. ObjEng::~ObjEng()
  71. {
  72.     PXTblClose(tHdl);
  73.     // Must close all of the tHdls in the RegisteredTables list.
  74.     for (int i = 0; i < OE_MAXTABLES; i++)
  75.     {
  76.         if (RegisteredTables[i].tHdl != 0) 
  77.             PXTblClose(RegisteredTables[i].tHdl);
  78.         else
  79.             break;
  80.     }
  81.     PXExit();
  82. }
  83.  
  84.  
  85. //-----------------------------------------------------------
  86. int ObjEng::TableInit()
  87. {
  88.     char dbname[76];
  89.     int bExist;
  90.  
  91.     strcpy(dbname, filedir);
  92.     strcat(dbname, "objeng");  //User name of objeng should cause error.
  93.     PXTblExist( dbname, &bExist );
  94.     if ( ! bExist ) 
  95.     {
  96.         char *fldnames[] = {"ID", "ChildID", "TblName"};
  97.         char *types[] = {"N", "N", "A12"};
  98.         FIELDHANDLE IdxFlds[] = { 1, 2 };
  99.         if ((status = PXTblCreate( dbname, 3, fldnames, types )) != PXSUCCESS) return status;
  100.         if ((status = PXKeyAdd( dbname, 2, IdxFlds, PRIMARY )) != PXSUCCESS) return status;
  101.         if ((status = PXTblOpen( dbname, &tHdl, 0, 1)) != PXSUCCESS) return status;
  102.         if ((status = PXRecBufOpen(tHdl, & rHdl)) != PXSUCCESS) return status;
  103.         PXPutLong(rHdl, 1, 0L);
  104.         PXPutLong(rHdl, 2, 10L);
  105.         PXPutAlpha(rHdl, 3, "Root");
  106.         PXRecInsert(tHdl, rHdl);
  107.         PXPutLong(rHdl, 1, 1L);  //Sub-object lists.
  108.         PXRecInsert(tHdl, rHdl);
  109.     }
  110.     else
  111.     {
  112.         if ((status = PXTblOpen( dbname, &tHdl, 0, 1)) != PXSUCCESS) return status;
  113.         if ((status = PXRecBufOpen(tHdl, & rHdl)) != PXSUCCESS) return status;
  114.     }
  115.     return status;
  116. }
  117.  
  118.  
  119. //-----------------------------------------------------------
  120. // The object engine management table has two records (ID=0 and ID=1)
  121. // to provide counters for Objects and for Lists (respectively).
  122. // After these two (starting at 10) come the List/TableName/Object tuples.
  123. // Each of these tuples represents an element on one list and its 
  124. // associated table name and objectID.  When a list is created it is empty.
  125. // As it is filled, this table stays in synch so it can be pulled out again
  126. // later.
  127. // To find any element on a list, find the record in the named data table
  128. // with the given objectID.  We must call a function (defined after the
  129. // objeng header has been processed) that lines up defined classes and 
  130. // the named tables.
  131. int ObjEng::GetIndex(long &index, int listFlag)
  132.     if (listFlag == TRUE) PXPutLong(rHdl, 1, 1L);
  133.     else PXPutLong(rHdl, 1, 0L);
  134.     if ((status = PXSrchKey(tHdl, rHdl, 1, SEARCHFIRST)) != PXSUCCESS) return status;
  135.     if ((status = PXRecGet(tHdl, rHdl)) != PXSUCCESS) return status;
  136.     PXGetLong(rHdl, 2, &index);
  137.     PXPutLong(rHdl, 2, index+1);
  138.     return (status = PXRecUpdate(tHdl, rHdl));
  139. }
  140.  
  141.  
  142. //-----------------------------------------------------------
  143. int ObjEng::AttachChild(long &parent, const char *tableName, const long &objectID)
  144.     PXPutLong(rHdl, 1, parent);
  145.     PXPutLong(rHdl, 2, objectID);
  146.     PXPutAlpha(rHdl, 3, (char *)tableName);
  147.     return(status = PXRecInsert(tHdl, rHdl));
  148. }
  149.  
  150.  
  151. //-----------------------------------------------------------
  152. int ObjEng::DetachChild(const long &parent, const long &child)
  153.     PXPutLong(rHdl, 1, parent);
  154.     PXPutLong(rHdl, 2, child);
  155.     if ((status = PXSrchKey(tHdl, rHdl, 2, SEARCHFIRST)) == PXSUCCESS)
  156.         status = PXRecDelete(tHdl);
  157.     return status;
  158. }
  159.  
  160.  
  161. //-----------------------------------------------------------
  162. int ObjEng::GetChild(long &parent, char *tbl, long &child, int mode)
  163.     // Given parent and mode, return tbl & child...
  164.     PXPutLong(rHdl, 1, parent);
  165.     if ((status = PXSrchKey(tHdl, rHdl, 1, mode)) != PXSUCCESS) return status;
  166.     if ((status = PXRecGet(tHdl, rHdl)) != PXSUCCESS) return status;
  167.     PXGetLong(rHdl, 2, &child);
  168.     PXGetAlpha(rHdl, 3, 12, tbl);
  169.     return status;
  170. }
  171.  
  172.  
  173. //-----------------------------------------------------------
  174. int ObjEng::Register(OEHandle &oehandle, char **FieldNames, char **FieldTypes, FIELDHANDLE *IdxFieldHandles, int nFields, int nIdxFields)
  175.     int bExist, i;
  176.     char dbname[76];
  177.  
  178.     // Rename using the file directory stored in dbcc
  179.     strcpy(dbname, filedir);
  180.     strcat(dbname, oehandle.TableName);
  181.     PXTblExist( dbname, &bExist );
  182.     if ( ! bExist )
  183.     {
  184.         // Create table and primary index
  185.         if ((status = PXTblCreate( dbname, nFields, FieldNames, FieldTypes)) != PXSUCCESS) return status;
  186.         if (nIdxFields > 0)
  187.         {
  188.             if ((status = PXKeyAdd( dbname, nIdxFields, IdxFieldHandles, PRIMARY )) != PXSUCCESS) return status;
  189.         }
  190.     }
  191.  
  192.     // Find match or advance to next open slot...
  193.     for (i = 0; i < OE_MAXTABLES; i++)
  194.     {
  195.         if ((strcmp(RegisteredTables[i].TableName, oehandle.TableName) == 0) ||
  196.             (RegisteredTables[i].tHdl == 0))
  197.             break;
  198.     }
  199.     // If nothing was found, then copy the tablename in and move on to the
  200.     // Table and Record Open fns to fill in the tHdl and rHdl vars.
  201.     if (RegisteredTables[i].tHdl == 0) 
  202.     {
  203.         strcpy(RegisteredTables[i].TableName, oehandle.TableName);
  204.         if ((status = PXTblOpen(dbname, &(RegisteredTables[i].tHdl), 0, OESaveChanges)) != PXSUCCESS) return status;
  205.         if ((status = PXRecBufOpen(RegisteredTables[i].tHdl, &(RegisteredTables[i].rHdl))) != PXSUCCESS) return status;
  206.     }
  207.     oehandle.tHdl = RegisteredTables[i].tHdl;
  208.     oehandle.rHdl = RegisteredTables[i].rHdl;
  209.     return status;
  210. }
  211.  
  212.  
  213. //-----------------------------------------------------------
  214. int ObjEng::IsRegistered(OEHandle &oehandle)
  215.     for (int i = 0; (i < OE_MAXTABLES) && (RegisteredTables[i].tHdl != 0); i++)
  216.     {
  217.         if (strcmp(RegisteredTables[i].TableName, oehandle.TableName) == 0)
  218.         {
  219.             oehandle.tHdl = RegisteredTables[i].tHdl;
  220.             oehandle.rHdl = RegisteredTables[i].rHdl;
  221.             return TRUE;
  222.         }
  223.     }
  224.     return FALSE;
  225. }
  226.  
  227. // $$$$$$$$$$$$$$$$$$$ Persistant Class Definitions $$$$$$$$$$$$$$$$$$$
  228.  
  229. //-----------------------------------------------------------
  230. PersistClass::PersistClass(char *name)
  231. {
  232.     objectID = 0;
  233.     status = PXSUCCESS;
  234.     memset(&oehandle, 0, sizeof(OEHandle));
  235.     strcpy(oehandle.TableName, name);
  236. }
  237.  
  238.  
  239. //-------------------------------------------------------
  240. void PersistClass::Copy(PersistClass &pc)
  241. {
  242.     objectID = pc.GetObjectID();
  243.     status = pc.status;
  244.     memcpy(&oehandle, &pc.oehandle, sizeof(OEHandle));
  245. }
  246.  
  247.  
  248. //-------------------------------------------------------
  249. void PersistClass::Unlink()
  250. {
  251.     objectID = 0;
  252.     status = PXSUCCESS;
  253. }
  254.  
  255.  
  256. //-------------------------------------------------------
  257. inline int PersistClass::NRecs(RECORDNUMBER & nrecs)
  258. {
  259.     return(status = PXTblNRecs(oehandle.tHdl, & nrecs));
  260. }
  261.  
  262.  
  263. //-----------------------------------------------------------
  264. inline int PersistClass::LinkToKey(int, int)
  265. {
  266.     return PXERR_TABLENOTINDEXED;
  267. }
  268.  
  269.  
  270. //-------------------------------------------------------
  271. int PersistClass::LinkToRecord(int position)
  272. {
  273.     RECORDNUMBER nrecs;
  274.  
  275.     NRecs(nrecs);
  276.     if ((position > nrecs) || (position < 1))
  277.         return(status = PXERR_OUTOFRANGE);
  278.  
  279.     if ((status = PXRecGoto(oehandle.tHdl, position)) == PXSUCCESS)
  280.         return Retrieve();
  281.     else 
  282.         return status;
  283. }
  284.  
  285.  
  286. //-----------------------------------------------------------
  287. int PersistClass::LinkToField(char *fieldname, void *value, int mode)
  288. {
  289.     FIELDHANDLE fld;
  290.  
  291.     if ((status = PXFldHandle(oehandle.tHdl, fieldname, &fld)) != PXSUCCESS) return status;
  292.  
  293.     switch (localtypes[fld - 1])  // handles start at 1, C arrays at 0
  294.     {
  295.         case 'D' : 
  296.             long dbdate;
  297.             struct tm *indate = (struct tm *)value;
  298.  
  299.             PXDateEncode(indate->tm_mon + 1, indate->tm_mday, indate->tm_year, & dbdate);
  300.             PXPutDate(oehandle.rHdl, fld, dbdate);
  301.             break;
  302.         case 'S' :
  303.             PXPutShort(oehandle.rHdl, fld, *(short *)value);
  304.             break;
  305.         case 'L' :
  306.             PXPutLong(oehandle.rHdl, fld, *(long *)value);
  307.             break;
  308.         case '$' :
  309.             PXPutDoub(oehandle.rHdl, fld, double(*(float *)value));
  310.             break;
  311.         case 'N' :
  312.             PXPutDoub(oehandle.rHdl, fld, *(double *)value);
  313.             break;
  314.         case 'A' :
  315.             PXPutAlpha(oehandle.rHdl, fld, (char *)value);
  316.             break;
  317.         default:
  318.             status = PXERR_INVFIELDHANDLE;
  319.             return(status);
  320.     }
  321.     if ((status = PXSrchFld(oehandle.tHdl, oehandle.rHdl, fld, mode)) == PXSUCCESS)
  322.         return Retrieve();
  323.     else 
  324.         return status;
  325. }
  326.  
  327.  
  328. //-----------------------------------------------------------
  329. int PersistClass::LinkToID(const long &ID)
  330. {
  331.     // Cannot use global nFields! (It changes for each derived class!)
  332.     int nFlds;
  333.  
  334.     if (ID == 0) // ID 0 is illegal
  335.     {
  336.         status = PXERR_RECNOTFOUND;
  337.         return status;
  338.     }
  339.     // THIS IS WHY the objectID must ALWAYS be the LAST field.
  340.     PXRecNFlds(oehandle.tHdl, &nFlds);
  341.     PXPutLong(oehandle.rHdl, nFlds, ID);
  342.     if ((status = PXSrchFld(oehandle.tHdl, oehandle.rHdl, nFlds, SEARCHFIRST)) == PXSUCCESS)
  343.         return Retrieve();
  344.     else 
  345.         return status;
  346. }
  347.  
  348.  
  349. //-------------------------------------------------------
  350. int PersistClass::LinkToFirst()
  351. {
  352.     RECORDNUMBER nrecs;
  353.  
  354.     NRecs(nrecs);
  355.     if ((status = PXRecGoto(oehandle.tHdl, nrecs)) == PXSUCCESS)
  356.         return Retrieve();
  357.     else
  358.         return status;
  359.  
  360.  
  361. //-------------------------------------------------------
  362. int PersistClass::LinkToLast()
  363. {
  364.     RECORDNUMBER nrecs;
  365.  
  366.     NRecs(nrecs);
  367.     if ((status = PXRecGoto(oehandle.tHdl, nrecs)) == PXSUCCESS)
  368.         return Retrieve();
  369.     else
  370.         return status;
  371.  
  372.  
  373. //-------------------------------------------------------
  374. int PersistClass::LinkToNext()
  375. {
  376.     if ((status = PXRecNext(oehandle.tHdl)) == PXSUCCESS)
  377.         return Retrieve();
  378.     else
  379.         return status;
  380. }
  381.  
  382.  
  383. //-------------------------------------------------------
  384. int PersistClass::LinkToPrev()
  385. {
  386.     if ((status = PXRecPrev(oehandle.tHdl)) == PXSUCCESS)
  387.         return Retrieve();
  388.     else
  389.         return status;
  390. }
  391.  
  392.  
  393. //-----------------------------------------------------------
  394. int PersistClass::Destroy()
  395. {
  396.     // Cannot use global nFields! (It changes for each derived class!)
  397.     int nFlds;
  398.  
  399.     if (objectID == 0) // Not yet added->can't be deleted.
  400.     {
  401.         status = PXERR_RECNOTFOUND;
  402.         return status;
  403.     }
  404.     // THIS IS WHY the objectID must ALWAYS be the LAST field.
  405.     PXRecNFlds(oehandle.tHdl, &nFlds);
  406.     PXPutLong(oehandle.rHdl, nFlds, objectID);
  407.     if ((status = PXSrchFld(oehandle.tHdl, oehandle.rHdl, nFlds, SEARCHFIRST)) != PXSUCCESS) return status;
  408.     objectID = 0;
  409.     return(status = PXRecDelete(oehandle.tHdl));
  410. }
  411.  
  412.  
  413. //-------------------------------------------------------
  414. int PersistClass::OEPutDate(const int &fld, const struct tm & indate)
  415. {
  416.     long dbdate;
  417.  
  418.     PXDateEncode(indate.tm_mon + 1, indate.tm_mday, indate.tm_year, & dbdate);
  419.     return (PXPutDate(oehandle.rHdl, fld, dbdate));
  420. }
  421.  
  422.  
  423. //-------------------------------------------------------
  424. int PersistClass::OEGetDate(const int &fld, struct tm & indate)
  425. {
  426.     long dbdate;
  427.  
  428.     if ((status = PXGetDate(oehandle.rHdl, fld, & dbdate)) == PXSUCCESS)
  429.     {
  430.         PXDateDecode(dbdate, & indate.tm_mon, & indate.tm_mday, & indate.tm_year);
  431.         indate.tm_mon--;
  432.     }
  433.     return status;
  434. }
  435.  
  436. // *******************************************************************
  437. // $$$$$$$$$$$$$$$$$$$ Container Class Definitions $$$$$$$$$$$$$$$$$$$
  438. // *******************************************************************
  439.  
  440. // *****************************************************************
  441. // Create a new (empty) OEList.
  442. // *****************************************************************
  443. OEList::OEList()
  444. {
  445.     listID = -1;
  446.     head = NULL;
  447.     cur = NULL;
  448.     referenceCount = 1;
  449.     status = NOTINIT;
  450. }
  451.  
  452.  
  453. // *****************************************************************
  454. // A list whose id is gotten from a parent being retrieved from 
  455. // the database.  
  456. // *****************************************************************
  457. OEList::OEList(long inID) : listID(inID)
  458. {
  459.     head = NULL;
  460.     cur = NULL;
  461.     referenceCount = 1;
  462.     status = NOTINIT;
  463. }
  464.  
  465.  
  466. // *****************************************************************
  467. // Initialize a list.  We must do this to ensure that whole descendent
  468. // sets (possibly multi-nested) are not retrieved when a single object
  469. // is retrieved.  If this isn't done then constructing a list whose
  470. // PersistClass elements contain lists will not work (the inner list
  471. // construction will screw the table up for the outer list construction).
  472. // Init a list: is no listID then this is virgin-- simply get a 
  473. // new listID.  Otherwise, find all of the associated children and 
  474. // place them in the array.
  475. // *****************************************************************
  476. int OEList::Init()
  477. {
  478.     char tbl[12];
  479.     long childid;
  480.     int mode = SEARCHFIRST;
  481.     PersistClass *temp;
  482.  
  483.     if (status == READY) return(TRUE);  //Allows multiple inits...
  484.     if (listID == -1)
  485.     {
  486.         if (objeng.GetIndex(listID, 1) != PXSUCCESS) 
  487.         {
  488.             status = FAIL;
  489.             return FALSE;
  490.         }
  491.         else
  492.             status = READY;
  493.     }
  494.     else
  495.     {
  496.         while (objeng.GetChild(listID, tbl, childid, mode) == PXSUCCESS)
  497.         {
  498.             // GetPersistClass takes a table name and an ID
  499.             // and retrieves a PersistClass descendent to place in pcArr.
  500.             // It also CREATES the actual object to be put in there (via NEW).
  501.             if (head == NULL)
  502.             {
  503.                 head = GetPersistClass(tbl, childid);
  504.                 cur = head;
  505.                 cur->next = NULL;
  506.             }
  507.             else
  508.             {
  509.                 temp = GetPersistClass(tbl, childid);
  510.                 cur->next = temp;
  511.                 temp->next = NULL;
  512.                 cur = temp;
  513.             }
  514.             cur->SetDeleteFlag(TRUE);
  515.             mode = SEARCHNEXT;
  516.         }
  517.         cur = head;
  518.         status = READY;
  519.     }
  520.     return(TRUE);
  521. }
  522.  
  523.  
  524. // *****************************************************************
  525. OEList::~OEList()
  526. {
  527.     PersistClass *temp;
  528.  
  529.     if (status != READY) return;
  530.     if (referenceCount > 1) 
  531.     {
  532. #ifdef _Windows
  533.         MessageBox(NULL, "Deleting a list without checking DeleteOK!", "Error", MB_OK | MB_ICONHAND);
  534. #else
  535. //        clrscr();
  536. //        printf("ERROR! Deleting a list without checking DeleteOK!");
  537. //        getch();
  538. #endif
  539.     }
  540.     cur = head;
  541.     while (cur != NULL)
  542.     {
  543.         temp = cur->next;
  544.         if (cur->GetDeleteFlag() == TRUE) delete cur;
  545.         cur = temp;
  546.     }
  547. }
  548.  
  549.  
  550. // ******************************************************************
  551. void OEList::Insert(PersistClass *curelem)
  552. {
  553.     PersistClass *temp;
  554.  
  555.     if (status == NOTINIT) Init();
  556.     if (status != READY) return;
  557.     // Must be stored FIRST to get an Object ID.
  558.     curelem->next = NULL;
  559.     curelem->Store();
  560.     // Now, insert the element after the currently pointed to element.
  561.     if (head == NULL)
  562.     {
  563.         head = curelem;
  564.         cur = curelem;
  565.     }
  566.     else
  567.     {
  568.         if (cur == NULL)  //Can't insert after null, place it last...
  569.         {
  570.             // Move to last LIVE element
  571.             cur = head;
  572.             while (cur->next != NULL)
  573.             {
  574.                 cur = cur->next;
  575.             }
  576.         }
  577.         // Now, insert it after the current element.
  578.         temp = cur->next;
  579.         cur->next = curelem;
  580.         curelem->next = temp;
  581.         // Now set cur to point to the new element...
  582.         cur = curelem;
  583.     }
  584.         
  585.     // Now set up corresponding object engine cross-link record.
  586.     objeng.AttachChild(listID, curelem->GetTblName(), curelem->GetObjectID());
  587. }
  588.  
  589.  
  590. // *****************************************************************************
  591. //  Remove the CURrent element from the list.
  592. // *****************************************************************************
  593. void OEList::Remove()
  594. {
  595.     PersistClass *temp;
  596.  
  597.     if (status == NOTINIT) Init();
  598.     if (status != READY) return;
  599.     if (cur == NULL)
  600.     {
  601.         return;
  602.     }
  603.     if (cur == head)
  604.     {
  605.         head = cur->next;
  606.         temp = cur->next;
  607.     }
  608.     else
  609.     {
  610.         // Zip through the elements until you get to the one right before cur...
  611.         temp = head;
  612.         while ((temp->next != NULL) && (temp->next != cur))
  613.         {
  614.             temp = temp->next;
  615.         }
  616.         if (temp->next == cur)  // JUST to make sure...
  617.         {
  618.             temp->next = cur->next;
  619.         }
  620.     }
  621.     // Delete the pointed-to entities
  622.     objeng.DetachChild(listID, cur->GetObjectID());
  623.     if (cur->GetDeleteFlag() == TRUE) delete cur;
  624.     // In effect, moves cur down one...
  625.     cur = temp;
  626. }
  627.  
  628.  
  629. // ******************************************************************
  630. PersistClass * OEList::operator()()
  631. {
  632.     if (status == NOTINIT) Init();
  633.     if (status != READY) return NULL;
  634.     return(cur);
  635. }
  636.  
  637.  
  638. // ******************************************************************
  639. PersistClass * OEList::operator++(int)
  640. {
  641.     PersistClass *temp;
  642.  
  643.     temp = cur;
  644.     if (cur != NULL) cur = cur->next;
  645.     return temp;
  646. }
  647.  
  648.  
  649. // ******************************************************************
  650. void OEList::Clear()
  651. {
  652.     PersistClass *temp;
  653.  
  654.     if (status == NOTINIT) Init();
  655.     if (status != READY) return;
  656.     // Zip through the elements and delete...
  657.     cur = head;
  658.     while (cur != NULL) 
  659.     {
  660.         temp = cur->next;
  661.         // Delete the pointed-to entities
  662.         objeng.DetachChild(listID, cur->GetObjectID());
  663.         if (cur->GetDeleteFlag() == TRUE) delete cur;
  664.         cur = temp;
  665.     }
  666. }
  667.  
  668.  
  669. // ******************************************************************
  670. int OEList::Count()
  671. {
  672.     PersistClass *temp;
  673.     int i = 0;
  674.  
  675.     if (status == NOTINIT) Init();
  676.     if (status != READY) return 0;
  677.     temp = head;
  678.     while (temp != NULL)
  679.     {
  680.         i++;
  681.         temp = temp->next;
  682.     }
  683.     return(i);
  684. }
  685.